<script setup lang="ts">
import { astar, Graph } from "javascript-astar";
import { off } from "process";
console.log(astar);
import {
  WebGLRenderer, //渲染器
  Scene, //场景
  PerspectiveCamera, //相机
  Color,
  Vector3,
  BufferGeometry,
  LineBasicMaterial,
  Line,
  SphereGeometry,
  MeshBasicMaterial,
  Mesh,
  BoxGeometry,
  AxesHelper,
  LineSegments,
  BufferAttribute,
  Vector2,
  Raycaster,
  PlaneGeometry,
  TextureLoader,
  MeshLambertMaterial,
  Plane,
  CatmullRomCurve3,
  Quaternion,
  Matrix4,
  Euler,
  DirectionalLight,
  AmbientLight, // 颜色转换
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { onMounted, onUnmounted, ref } from "vue";
import { useAddEvent } from "@/hooks/useAddEvent";
const ws = 4; // 每个格子占据像素点
const length = 100; // 棋盘大小
//棋盘中心点位于 原点（0，0，0）
/**
 * -****
 * *****
 * **·**
 * *****
 * -****
 */
const startNate = 0; // (length / 2) * -1; // 那么起点就是 负棋盘大小的一半
const endNate = length; //length / 2; // 终点同理
const width = window.innerWidth;
const height = window.innerHeight;
const container = ref<HTMLElement | null>(null); // 插入的dom元素
const scene: Scene = new Scene(); // 场景
const camera = new PerspectiveCamera(60, width / height, 1, 10000); // 镜头
const initCamera = [length / 2, length * 2, length / 2] as const;

const { initMouse, initKeyDown, isCustomLook, left, right, front, back } = useAddEvent(
  camera
);
initMouse();
initKeyDown();
camera.position.set(...initCamera); // 垂直视角，便于观看棋盘
const [x, y, z] = initCamera;
camera.lookAt(x, 0, z);
const renderer = new WebGLRenderer(); // 渲染器
const controls = ref();
type cameraType = "one" | "three";
const cameraStatus = ref<cameraType>("one");
const grap = ref<number[][]>([]); // 矩形树
const grapMap = ref<Graph>(); //
const activeLine = ref<any>(); // 当前需要移动的路径
const activeLinePoints = ref<Vector3[]>([]); // 当前移动路径的点
const ren = ref();
const activeInt = ref();
const isMotion = ref<boolean>();

// 添加坐标系辅助，后面可以删除
const addAxes = () => {
  const axisHelper = new AxesHelper(2000);
  axisHelper.name = "axis";
  scene.add(axisHelper); // 把创建的坐标体系加入天地！
  renderer.render(scene, camera); // 重新渲染
};
addAxes();

const adddeng = () => {
  // 环境光
  var ambientLight = new AmbientLight(0x606060);
  scene.add(ambientLight);
  // 平行光
  var directionalLight = new DirectionalLight(0xbcd2ee);
  directionalLight.position.set(1, 0.75, 0.5).normalize();
  scene.add(directionalLight);
};
adddeng();

// const addPlane = () => {
//   let planeGeometry = new PlaneGeometry(1000, 1000); //地面的材质，宽高
//   const tietu =
//     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F4415327a-1d82-4985-8302-44b07a8498c0%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687512010&t=df81831ff29236e2796899fdcaf43b74";
//   const texLoader = new TextureLoader();
//   // .load()方法加载图像，返回一个纹理对象Texture
//   const texture = texLoader.load(tietu);
//   const material = new MeshLambertMaterial({
//     // 设置纹理贴图：Texture对象作为材质map属性的属性值
//     map: texture, //map表示材质的颜色贴图属性
//   });

//   let planeMaterial = new MeshBasicMaterial({ color: new Color("#666666") });
//   let plane = new Mesh(planeGeometry, planeMaterial); // 设置地面的位置
//   plane.name = "plane";
//   plane.rotation.x = -0.5 * Math.PI; // 倾斜把竖着的垂直下来，具体参考圆周率
//   plane.position.y = -10;
//   scene.add(plane); //将地面添加到坐标轴中
// };
// addPlane();

const initControls = () => {
  controls.value = new OrbitControls(camera, renderer.domElement);
  // controls.value.target = camera.position;
  controls.value.update();
  controls.value.minDistance = 50;
  controls.value.maxDistance = 500;
  controls.value.maxPolarAngle = Math.PI / 2;
  controls.value.minAzimuthAngle = -Math.PI / 2;
  controls.value.maxAzimuthAngle = Math.PI / 2;
  controls.value.addEventListener("change", (e) => {
    //  camera.lookAt(length / 2, 0, length / 2 - 1); //重新计算相机视线方向
    const dis = controls.value.getDistance();
    console.log("dis", dis);
    renderer.render(scene, camera);
  });
};

onMounted(() => {
  (container.value as HTMLElement).appendChild(renderer.domElement); // 载入到dom
  scene.background = new Color("#ffffff"); // 设置背景色
  renderer.setSize(width, height);

  // camera.lookAt(ren.value.position);
  // camera.lookAt(length / 2, 0, length / 2 - 1); //重新计算相机视线方向

  renderer.render(scene, camera); // 重新渲染
  container.value.addEventListener("dblclick", onClick, false);
  window.addEventListener("resize", onResize, false);
});

onUnmounted(() => {
  clearInterval(activeInt.value);
  container.value?.removeEventListener("dblclick", onClick, false);
  window.removeEventListener("resize", onResize, false);
});

function onResize() {
  camera.aspect = window.innerWidth / height;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, height);
}
// 监听调整大小事件

const initGround = () => {
  var geometry = new BufferGeometry(); // 生成一个空的几何体对象
  const pointsArray = [new Vector3(startNate, 0, 0), new Vector3(endNate, 0, 0)]; // 创建顶点
  geometry.setFromPoints(pointsArray);

  for (var i = 0; i <= length / ws; i++) {
    // 先生成横着的线，这个比较好处理，就是从起点每次增加的格子，不断添加即可
    var heng = new Line(
      geometry,
      new LineBasicMaterial({ color: new Color("#888"), opacity: 0.1 })
    );
    heng.position.z = startNate + i * ws;
    //scene.add(heng);

    // 竖线稍微复杂的，其实也是先画横着的线，然后再旋转下即可
    var shu = new Line(
      geometry,
      new LineBasicMaterial({ color: new Color("#888"), opacity: 1 })
    );
    shu.position.x = startNate + i * ws;
    shu.position.z = length; // 如果是以中心点为轴 ，则步需要z轴动
    shu.rotation.y = (90 * Math.PI) / 180;

    //  scene.add(shu);
  }
};
initGround();

const addRen = () => {
  const geometry = new BoxGeometry(ws, 2, ws);
  var cube = new Mesh(
    geometry,
    new MeshBasicMaterial({
      color: 0xff0000,
    })
  );
  // 把人定位到起点
  let x = startNate + ws / 2;
  let z = startNate + ws / 2;
  cube.position.set(x, 1, z);
  cube.name = "ren";
  ren.value = cube;
  scene.add(cube);
};
addRen();

// 只需要把格子横竖循环下，即可生成一个 graph模型
function initGrid() {
  for (var i = 0; i < length / ws; i++) {
    var nodeRow = [];
    // 注意一排一排生成，与 astar 坐标系一致
    for (var j = 0; j < length / ws; j++) {
      const random = Math.random();

      if (random > 0.2) {
        nodeRow.push(1);
      } else {
        const geometry = new BoxGeometry(ws, 30, ws);
        var cube = new Mesh(
          geometry,
          new MeshBasicMaterial({
            color: 0xcccccc,
          })
        );
        let x = ws * j + ws / 2;
        let z = ws * i + ws / 2;
        cube.position.set(x, 0.1, z); // y轴高一点，不然与地面重叠，会闪烁
        cube.name = "obstacle";
        scene.add(cube);
        nodeRow.push(0);
      }
    }

    grap.value.push(nodeRow);
  }
  grapMap.value = new Graph(grap.value, { diagonal: false });
}
initGrid();

// 删除路径
const deleteLine = () => {
  let child = scene.children; //获取场景中的所有子对象
  for (var i = 0; i < child.length; i++) {
    // 把路径和障碍物清除
    if (child[i].name === "lujin") {
      scene.remove(child[i]); //从场景中移除
      i--;
    }
  }
};

// 根据路线获取顶点数据,并且生成线路
const getDd = (arr: GridNode[]) => {
  const position = ren.value.position;
  activeLinePoints.value = [position];
  deleteLine();
  arr.forEach((item) => {
    const { x, y } = item; // 注意x是行，y 是列
    const x1 = x * ws + ws / 2;
    const z1 = y * ws + ws / 2;
    activeLinePoints.value.push(new Vector3(z1, 0, x1));
  });

  const geometry = new BufferGeometry();
  geometry.setFromPoints(activeLinePoints.value);
  const material = new LineBasicMaterial({
    color: 0x000000, //线条颜色
  });
  const line = new Line(geometry, material);
  line.name = "lujin";
  scene.add(line);
  isCustomLook.value = false;
  move();
};

const move = () => {
  const curve = new CatmullRomCurve3(activeLinePoints.value);
  const pointsArr = curve.getSpacedPoints(10);

  const velocity = 0.01;
  let index = velocity;

  activeInt.value = setInterval(() => {
    const nextSize = index - velocity;
    if (index <= 1) {
      const point = curve.getPointAt(index); //获取样条曲线指定点坐标

      const pointBox = curve.getPointAt(nextSize); //获取下一个节点坐标
      if (!pointBox || !point) return;
      try {
        ren.value.position.set(point.x, point.y, point.z);
      } catch (err) {
        console.log(err);
      }
      var targetPos = pointBox; //目标位置点
      var offsetAngle = 0; //目标移动时的朝向偏移

      var mtx = new Matrix4(); //创建一个4维矩阵
      // .lookAt ( eye : Vector3, target : Vector3, up : Vector3 ) : this,构造一个旋转矩阵，从eye 指向 target，由向量 up 定向。
      mtx.lookAt(ren.value.position.clone(), targetPos, ren.value.up); //设置朝向

      mtx.multiply(new Matrix4().makeRotationFromEuler(new Euler(0, offsetAngle, 0)));
      var toRot = new Quaternion().setFromRotationMatrix(mtx); //计算出需要进行旋转的四元数值
      ren.value.quaternion.slerp(toRot, 0.2);

      const { x, y, z } = ren.value.position.clone();
      if (cameraStatus.value === "one") {
        var pos = curve.getPointAt(index - velocity);
        camera.position.set(pos.x, 10, pos.z);
        if (!isCustomLook.value) {
          camera.lookAt(pointBox);
        }

        renderer.render(scene, camera);
      }

      index += velocity;
    } else {
      clearInterval(activeInt.value);
    }
  }, 50);
};

const handleClick = () => {
  var sta = grapMap.value.grid[0][0];
  var end = grapMap.value.grid[4][1];
  var result = astar.search(grapMap.value, sta, end);
  getDd(result);
  renderer.render(scene, camera); // 重新渲染
};

const cleanSphere = () => {
  let child = scene.children; //获取场景中的所有子对象
  for (var i = 0; i < child.length; i++) {
    // 把路径和障碍物清除
    if (child[i].name === "obstacle" || child[i].name === "lujin") {
      scene.remove(child[i]); //从场景中移除
      i--;
    }
  }
};

const handleGenerate = () => {
  grap.value = [];
  cleanSphere();
  initGrid();
  renderer.render(scene, camera); // 重新渲染
};

const onClick = (event) => {
  clearInterval(activeInt.value);
  grapMap.value = new Graph(grap.value, { diagonal: false });
  var raycaster = new Raycaster();
  var vector = new Vector2(
    (event.clientX / width) * 2 - 1,
    -(event.clientY / height) * 2 + 1
  );
  var fxl = new Vector3(0, 1, 0);
  var groundplane = new Plane(fxl, 0);
  raycaster.setFromCamera(vector, camera);
  var ray = raycaster.ray;
  let intersects = ray.intersectPlane(groundplane, fxl);
  if (!intersects) return;
  let { x, z } = intersects;

  if (x > length || z > length || x < 0 || z < 0) return;

  const x1 = Math.floor(x / ws);
  const z1 = Math.floor(z / ws);
  console.log(ren.value);
  const position = ren.value.position;

  const x2 = Math.floor(position.x / ws);
  const z2 = Math.floor(position.z / ws);

  var sta = grapMap.value.grid[z2][x2];
  var end = grapMap.value.grid[z1][x1];
  var result = astar.search(grapMap.value, sta, end);
  if (!result || result.length === 0) {
    console.log(`第${z2}行第${x2}列 移动到 第${z1}行第${x1}列`);
    console.log(grapMap.value);
    console.log(grap.value);
    console.log("没找到路");
    return;
  }
  console.log(ren.value);
  // camera.position.set(...ren.value.position.clone());
  //camera.lookAt(ren.value.up); //设置朝向
  // camera.rotateOnWorldAxis(new Vector3(0, 1, 0), Math.PI);
  getDd(result);
  renderer.render(scene, camera); // 重新渲染
};

function animate() {
  requestAnimationFrame(animate);
  // if (controls.value) {
  //   controls.value.update();
  // }
  // let vect = camera.getWorldDirection(new Vector3());
  // if (front.value) {
  //   camera.position.z += vect.dot(new Vector3(0, 0, 15)) * 0.01;
  //   camera.position.x += vect.dot(new Vector3(15, 0, 0)) * 0.01;
  // }
  renderer.render(scene, camera);
}
animate();

const updateCamere = () => {
  cameraStatus.value = cameraStatus.value === "three" ? "one" : "three";
  if (cameraStatus.value === "three") {
    camera.position.set(...initCamera);
  }
};
const RotateY = () => {
  camera.rotateX(0.5);
};
const rotateOnAxis = () => {
  camera.rotateOnAxis(new Vector3(0, 1, 0), 0.5);
};
const rotateOnWorldAxis = () => {
  camera.rotateOnWorldAxis(new Vector3(0, 1, 0), 0.6);
};
</script>

<template>
  <div class="three">
    <div ref="container"></div>
    <div class="btns">
      <el-button @click="updateCamere">{{
        cameraStatus === "three" ? "第三人称视角" : "第一人称视角"
      }}</el-button>
      <el-button @click="handleGenerate">随机生成{{ isCustomLook }}</el-button>
      <el-button @click="RotateY">RotateY</el-button>
      <el-button @click="rotateOnAxis">rotateOnAxis</el-button>
      <el-button @click="rotateOnWorldAxis">rotateOnWorldAxis</el-button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.three {
  overflow: hidden;
}
.btns {
  position: fixed;
  right: 0;
  top: 0;
  padding: 10px;
}
</style>
